#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <ctime>

template<typename T>
class circularQ
{
private:
    void trimsem(sem_t &s)
    {
        sem_wait(&s);
    }

    void addsem(sem_t &s)
    {
        sem_post(&s);
    }
public:
    circularQ(int capacity)
        :_circularQ(capacity)
        ,_capacity(capacity)
        ,_csubsofQ(0)
        ,_psubsofQ(0)
    {
        sem_init(&_data_sem, 0, 0);
        sem_init(&_space_sem, 0, capacity);
        pthread_mutex_init(&_cmutex, nullptr);
        pthread_mutex_init(&_pmutex, nullptr);
    }

    void enqueue(const T& in)
    {
        trimsem(_space_sem);
        pthread_mutex_lock(&_pmutex);
        _circularQ[_psubsofQ] = in;
        _psubsofQ++;
        _psubsofQ %= _capacity;
        std::cout << "productor -> " << in << std::endl;
        pthread_mutex_unlock(&_pmutex);
        addsem(_data_sem);
    }

    // 注意队列时先进先出的规则，所以插入数据和释放数据时，下标都是 ++ 的
    void dequeue(T *out)
    {
        trimsem(_data_sem);
        pthread_mutex_lock(&_cmutex);
        *out = _circularQ[_csubsofQ];
        _csubsofQ++;
        _csubsofQ %= _capacity;
        std::cout << "consumer -> " << *out << std::endl;
        pthread_mutex_unlock(&_cmutex);
        addsem(_space_sem);
    }

    ~circularQ()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_space_sem);
        pthread_mutex_destroy(&_cmutex);
        pthread_mutex_destroy(&_pmutex);
    }
private:
    std::vector<T> _circularQ;
    int _capacity;

    int _csubsofQ;
    int _psubsofQ;

    sem_t _data_sem;
    sem_t _space_sem;

    pthread_mutex_t _cmutex;
    pthread_mutex_t _pmutex;
};

void *consumer(void *args)
{
    circularQ<int> *cq = static_cast<circularQ<int> *>(args);
    while (true)
    {
        // int data = 0;
        int i;
        // 1. 消费
        cq->dequeue(&i);

        // 2. 处理数据
        // std::cout << "consumer -> " << i << std::endl;
    }
    
}

void *productor(void *args)
{
    circularQ<int> *cq = static_cast<circularQ<int> *>(args);
    int i = 1;
    int n = 10000;
    while (i < n)
    {
        sleep(1);
        // int data = rand() % 10 + 1;
        // cq->enqueue(data);
        cq->enqueue(i);

        // std::cout << "productor -> " << i << std::endl;
        i++;
    }
    return nullptr;
}

int main()
{
    srand(time(nullptr) ^ getpid());
    circularQ<int> *cq = new circularQ<int>(5);

    // 单生产，单消费
    pthread_t c1, c2, p1, p2, p3;
    pthread_create(&c1, nullptr, consumer, cq);
    pthread_create(&c2, nullptr, consumer, cq);
    pthread_create(&p1, nullptr, productor, cq);
    pthread_create(&p2, nullptr, productor, cq);
    pthread_create(&p3, nullptr, productor, cq);

    pthread_join(c1, nullptr);
    pthread_join(c2, nullptr);
    pthread_join(p1, nullptr);
    pthread_join(p2, nullptr);
    pthread_join(p3, nullptr);

    return 0;
}